<template>
  <div class="tree-graph-container">
    <div class="flex">
      <el-button type="primary" @click="handleCreatNode"> 创建节点 </el-button>
      <el-button type="primary" @click="handleJoinNode"> 连接节点 </el-button>
      <el-button type="primary" @click="test4"> 四种添加边的方式 </el-button>
    </div>

    <div class="flex flex-row">
      <!-- 1. x6 容器 -->
      <div id="x6Container" ref="containerRef"></div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref, shallowRef } from "vue";
import { Graph, Shape } from "@antv/x6";
import { Export } from "@antv/x6-plugin-export";

let graph: Graph | null = null;

// x6 图的实例
const graphInstance = shallowRef();

const containerRef = ref();

const node1 = {
  id: "node1", // String，可选，节点的唯一标识
  shape: "rect", // 使用 rect 渲染 https://x6.antv.vision/zh/examples/node/native-node#rect
  x: 100, // Number，必选，节点位置的 x 值
  y: 200, // Number，必选，节点位置的 y 值
  width: 80, // Number，可选，节点大小的 width 值
  height: 40, // Number，可选，节点大小的 height 值
  label: "矩形1" // String，节点标签
};

const node2 = {
  id: "node2", // String，可选，节点的唯一标识
  shape: "rect", // 使用 rect 渲染 https://x6.antv.vision/zh/examples/node/native-node#rect
  x: 100, // Number，必选，节点位置的 x 值
  y: 600, // Number，必选，节点位置的 y 值
  width: 80, // Number，可选，节点大小的 width 值
  height: 40, // Number，可选，节点大小的 height 值
  label: "矩形2" // String，节点标签
};

const node3 = {
  id: "node3", // String，可选，节点的唯一标识
  shape: "rect", // 使用 rect 渲染 https://x6.antv.vision/zh/examples/node/native-node#rect
  x: 100, // Number，必选，节点位置的 x 值
  y: 600, // Number，必选，节点位置的 y 值
  width: 80, // Number，可选，节点大小的 width 值
  height: 40, // Number，可选，节点大小的 height 值
  label: "矩形2" // String，节点标签
};

// 创建节点
const rect21 = new Shape.Rect(node1);
const rect22 = new Shape.Rect(node2);
const rect33 = new Shape.Rect(node3);
const edge = new Shape.Edge();
const handleCreatNode = () => {
  graph!.addNode(rect21).position(200, 100);
  graph!.addNode(rect22).position(200, 300);
  graph!.addNode(rect33).position(200, 500);
};

const handleJoinNode = () => {
  edge.setSource(rect21).setTarget(rect22);
  graph!.addEdge(edge);

  graph!.addEdge({
    shape: "edge", // 指定使用何种图形，默认值为 'edge' 双线边：double-edge 阴影边：shadow-edge
    source: rect22,
    target: rect33,
    label: "默认边"
  });
};

const test4 = () => {
  // 创建节点
  const rect1 = new Shape.Rect({
    id: "rect1",
    x: 400,
    y: 100,
    width: 80,
    height: 40,
    label: "rect1"
  });
  graph.addNode(rect1);
  const rect2 = new Shape.Rect({
    id: "rect2",
    x: 400,
    y: 200,
    width: 80,
    height: 40,
    label: "rect2"
  });
  graph.addNode(rect2);
  // 第一种方式
  graph.addEdge({
    source: rect1, // 源节点
    target: rect2, // 目标节点
    label: "通过节点方式添加边"
  });
  const rect3 = new Shape.Rect({
    id: "rect3",
    ports: [
      {
        id: "out-port-1",
        attrs: {
          circle: {
            r: 6,
            magnet: true, //使链接桩在连线交互时可以被连接上
            stroke: "#31d0c6",
            strokeWidth: 2,
            fill: "#fff"
          }
        }
      }
    ],
    x: 400,
    y: 300,
    width: 80,
    height: 40,
    label: "rect3"
  });
  graph.addNode(rect3);

  // 第二种方式
  graph.addEdge({
    source: "rect2", // 源节点
    target: "rect3", // 目标节点
    label: "通过节点id的方式添加边"
  });
  // 第三种方式
  const rect4 = new Shape.Rect({
    id: "rect4",
    ports: [
      {
        id: "in-port-1",
        attrs: {
          circle: {
            r: 6,
            magnet: true, //使链接桩在连线交互时可以被连接上
            stroke: "#31d0c6",
            strokeWidth: 2,
            fill: "#fff"
          }
        }
      }
    ],
    x: 400,
    y: 400,
    width: 80,
    height: 40,
    label: "rect4"
  });
  graph.addNode(rect4);
  graph.addEdge({
    source: { cell: rect3, port: "out-port-1" }, // 源节点和链接桩 ID
    target: { cell: "rect4", port: "in-port-1" }, // 目标节点 ID 和链接桩 ID
    // （当源/目标是画布上的点时，需要开启 allowBlank 选项（默认已经开启）才能生效）（画布属性）
    label: "通过链接桩的方式添加"
  });
  // 第四种方式
  graph.addEdge({
    source: "rect4", // 源节点 ID
    target: { x: 438, y: 500 }, // 目标点
    label: "target为坐标"
  });
};

// 1.初始化数据
const originalData = ref({
  // 节点
  nodes: [],
  // 边
  edges: []
});
// 2.初始化画布
const init = () => {
  // 初始化 x6 图背景板
  // 创建一个新的 Graph 实例
  graph = new Graph({
    // 指定 Graph 的容器元素
    container: document.getElementById("x6Container")!,

    // 自动调整 Graph 的大小以适应容器
    autoResize: true,

    // 设置背景颜色
    background: {
      color: "#fffbe6" // 设置画布背景颜色
    },
    // 网格设置
    grid: {
      visible: true, // 渲染网格背景
      size: 10, // 网格大小 10px
      type: "doubleMesh",
      args: [
        {
          color: "#eee", // 主网格线颜色
          thickness: 1 // 主网格线宽度
        },
        {
          color: "#ddd", // 次网格线颜色
          thickness: 1, // 次网格线宽度
          factor: 4 // 主次网格线间隔
        }
      ]
    },

    // 滚动设置
    scroller: {
      enabled: false, // 启用滚动
      pannable: true // 允许通过拖动来平移视图
    },

    // 是否启用缩略图（小地图）
    minimap: false, // 禁用缩略图

    // 鼠标滚轮设置
    mousewheel: {
      enabled: true, // 启用鼠标滚轮缩放
      modifiers: ["ctrl", "meta"] // 可以通过按下 Ctrl 或 Meta 键来启用缩放
    },

    // 平移设置
    panning: {
      enabled: true, // 启用平移功能
      modifiers: "space" // 需要按住 space 键才能进行拖拽平移 大空格键
    },

    // 交互设置(拖拽移动)
    interacting: {
      nodeMovable: true
    },

    // 拖拽设置
    dragging: {
      enabled: true, // 启用拖拽功能
      ghost: false // 禁用拖拽时的虚影效果
    },

    // 当源/目标是画布上的点时，需要开启 allowBlank 选项（默认已经开启）才能生效
    connecting: {
      allowBlank: false
    }
  });

  // 渲染
  graph.fromJSON(originalData.value);
  graph.zoomToFit(); // 缩放
  graph.scale(0.7); // 设置初始缩放比例，例如 0.7 表示缩小到 70%
  // graph.centerContent(); // 居中
  graph.use(new Export()); // 加载导出插件
  graphInstance.value = graph; // 保存图实例
};

onMounted(() => {
  init(); // 初始化树数据
});
</script>

<style scoped>
#x6Container {
  flex: 1;
  width: 100%;
  height: 100vh;
  position: relative;
}
</style>
